//Heuristic that returns a value of 0 and 10, being 0 the worst scenario

import java.math.*;
import java.util.*;


public class Metaheuristic{
                
        
        public static double VALID_TIMESTAMP = 0.5;
       
        public int GetBestHeuristic()
        {
        	
        	return -1;
        }

        public static void fillZone(int n, int zone[]) {
                zone[n]=0;
                int contUp=n++;
                int contDown=n--;

                if(contUp>7)
                        contUp=0;
                zone[contUp++]-=2;

                if(contUp>7)
                        zone[0]-=5;
                else
                        zone[contUp]-=5;

                if(contDown<0)
                        contDown=7;
                zone[contDown--]-=2;

                if(contDown<0)
                        zone[7]-=5;
                else
                        zone[contDown]-=5;
        }

        public double calculatePass(Player p, World wm ){ //calculo para la matriz de pesos
    		double cP=0;
    		int n = p.getNumber();
    		int max = Integer.parseInt(p.getPlayerParam("visible_distance"));
    		int i;
    		double sumOp = 0 , sumAl = 0;
    		wm= p.getWorldModel();
    			
    		for (i=1; i<11; i++){
    			sumOp = ((wm.getOpponentDistance(i)/max)*((8/Math.pow(10,5))*Math.pow((wm.getOpponentTimestamp(i)-p.getTime()),2)))+sumOp;
    			
    			
    			if (n != i)
    			{
    				sumAl = ((1-((wm.getAllyDistance(i)/max)))*((8/Math.pow(10,5))*Math.pow((wm.getAllyTimestamp(i)-p.getTime()),2)))+sumAl;
    			}
    					
    		}
    		
    		cP=sumOp+sumAl;
    		
    		System.out.println(Double.toString(cP));
    		
    		return cP; //Valor que va en la Matriz de Pesos
    	}
        
        
    	
		public static void calculateToZone(Player p, int zone[], double moveWithBallInfo[]){
                double headAngle = p.getAngle(); 
                int headZone = (int)Math.floor(headAngle/45);
                if(headZone<0)
                        headZone=Math.abs(headZone)+4;
                
                double moveForward = zone[headZone];
                headZone+=2;
                if(headZone<7)
                        headZone-=8;
                double moveRight = zone[headZone];
                headZone+=2;
                if(headZone<7)
                        headZone-=8;
                double moveBack = zone[headZone];
                headZone+=2;
                if(headZone<7)
                        headZone-=8;
                double moveLeft = zone[headZone];
                headZone+=2;
                if(headZone<7)
                        headZone-=8;
			
//				moveForward*=p.WEIGHTS;
//				moveLeft*=p.WEIGHTS;
//				moveBack*=p.WEIGHTS;
//				moveRight*=p.WEIGHTS;

				moveWithBallInfo[0]=moveForward;
				moveWithBallInfo[1]=moveLeft;
				moveWithBallInfo[2]=moveBack;
				moveWithBallInfo[3]=moveRight;
		}

        
        public static int moveWithBallHeuristic(Player p, double moveWithBallInfo[], World worldModel){ 
                int zone[]={0,0,0,0,0,0,0,0};
                //Array that defines the heurstics for each zone
                //0 -> Angle between 0 and 45
                //1 -> Angle between 45 and 90
                //2 -> Angle between 90 and 135 ...
                
                //Ball within kickable margin
                if(worldModel.getBallDistance()> 0.7)
                        return 0;
                        
                for(int i=1; i<12; i++)
                        //Valiate opponent timestamp
                        if(worldModel.getOpponentTimestamp(i)> VALID_TIMESTAMP)
                                if(worldModel.getOpponentDistance(i)< 10){
                                        //Distance of opponent is short so zones near him are blocked
                                        int angleZone = (int)Math.floor(worldModel.getWorld()[1][i].direction/45);
                                        if(angleZone < 0)
                                                angleZone = Math.abs(angleZone)+4;
                                        fillZone(angleZone,zone);
                                }       
                for(int i=0;i<8;i++)
                        if(zone[i]<0)
                                zone[i]=0;
                
				calculateToZone(p, zone, moveWithBallInfo);
				
				int maxValue=(int)Math.round(moveWithBallInfo[0]);
				for(int i=1;i<4;i++)
						if(moveWithBallInfo[i]>maxValue)
							maxValue=(int)Math.round(moveWithBallInfo[i]);

                return maxValue;
        }
        
        public static void InitShootingAnglesAndTrajectories(Player p, ShootingMetaData shootData)
        {
        	double[] values = new double[4];
        	

            shootData.distTop = Math.sqrt(Auxiliar.SQR(52-Math.abs(p.getX()))+Auxiliar.SQR(Math.abs(p.getY())+7));
            shootData.distBottom = Math.sqrt(Auxiliar.SQR(52-Math.abs(p.getX()))+Auxiliar.SQR(Math.abs(p.getY())-7));

            shootData.angleTop = Math.acos((Auxiliar.SQR(values[0])+Auxiliar.SQR(shootData.distanceGoal)-Auxiliar.SQR(7))/(2*values[0]*shootData.distanceGoal));
            shootData.angleBottom = (-1)*Math.acos((Auxiliar.SQR(values[1])+Auxiliar.SQR(shootData.distanceGoal)-Auxiliar.SQR(7))/(2*values[1]*shootData.distanceGoal));
            shootData.shootAngle=Math.acos((Auxiliar.SQR(shootData.distTop)+Auxiliar.SQR(shootData.distBottom)-Auxiliar.SQR(14))/(2*shootData.distTop*shootData.distBottom));
        }
        
        private static void InitTrajectoryFreedom(World worldModel, ShootingMetaData shootData)
        {
            for(int i=1;i<12;i++){
                    if(worldModel.getAllyTimestamp(i)>0.5){
                            if(worldModel.getAllyAngle(i)<shootData.angleTop)
                                    if(worldModel.getAllyAngle(i)>(shootData.angleTop-3))
                                    	shootData.topTrajectoryFree=false;
                            else if(worldModel.getAllyAngle(i)>shootData.angleBottom)
                                    if(worldModel.getAllyAngle(i)<(shootData.angleBottom+3))
                                    	shootData.bottomTrajectoryFree=false;}
            }
            for(int i=1;i<12;i++)
                    if(worldModel.getOpponentTimestamp(i)>0.5){
                            if(worldModel.getOpponentAngle(i)<shootData.angleTop)
                                    if(worldModel.getOpponentAngle(i)>(shootData.angleTop-3))
                                    	shootData.topTrajectoryFree=false;
                            else if(worldModel.getOpponentAngle(i)>shootData.angleBottom)
                                    if(worldModel.getOpponentAngle(i)<(shootData.angleBottom+3))
                                    	shootData.bottomTrajectoryFree=false;}
        	
        }
        
        private static int GetTopTrajectoryValue(World worldModel, ShootingMetaData shootData, double[] kickInfo)
        {
            if(shootData.topTrajectoryFree)
            {
                //Search if the goalie is more to the bottom or lower possible trajectory
                if(worldModel.getOpponentAngle(1)>0)
                {
                        //Upper trajectory with no obstacles and goalie in the bottom zone
                        kickInfo[0]=shootData.angleTop;
                        kickInfo[1]=shootData.distTop;
                        return 10;
                }
                else
                {
                        //Upper trajectory with no obstacles, but goalie in this zone
                        kickInfo[0]=shootData.angleTop;
                        kickInfo[1]=shootData.distTop;
                        return 7;
                }
            }
            else if(shootData.bottomTrajectoryFree)
            {
                if(worldModel.getOpponentAngle(1)>0)
                {
                        //Lower trajectory with no obstacles and goalie in the top zone
                        kickInfo[0]=shootData.angleBottom;
                        kickInfo[1]=shootData.distBottom;
                        return 5;
                }        	
            }

            return -3;
        	
        }
        
        private static int GetBottomTrajectoryValue(World worldModel, ShootingMetaData shootData, double[] kickInfo)
        {
        	if(shootData.bottomTrajectoryFree)
                //Search if the goalie is more to the bottom or lower possible trajectory
                if(worldModel.getOpponentAngle(1)<0){
                        //Lower trajectory with no obstacles and goalie in the top zone
                        kickInfo[0]=shootData.angleBottom;
                        kickInfo[1]=shootData.distBottom;
                        return 10;
                        }
                else{
                        //Bottom trajectory with no obstacles, but goalie in this zone
                        kickInfo[0]=shootData.angleBottom;
                        kickInfo[1]=shootData.distBottom;
                        return 7;
                        }
        	else if(shootData.topTrajectoryFree)
        	{
                if(worldModel.getOpponentAngle(1)>0)
                {
                        //Lower trajectory with no obstacles and goalie in the bottom zone
                        kickInfo[0]=shootData.angleTop;
                        kickInfo[1]=shootData.distTop;
                        return 5;
                }
        	}
        	return -3;
        	
        }
        
        
        public static int shootToGoalHeuristic(Player p, double kickInfo[], World worldModel){ 
//				if(!haveBallPossesion()) {return -3;}

        		ShootingMetaData shootData = new ShootingMetaData();
        		
                shootData.distanceGoal = worldModel.getOpponentDistance(12);
				System.out.println("Dist: " + shootData.distanceGoal);
				System.out.println("Dir: " + (int)worldModel.getOpponentAngle(12));

                if(shootData.distanceGoal>20){return -1;}
                
                //Turn to the goal
                p.turn((int)worldModel.getOpponentAngle(12));
				System.out.println("Lo que debo de hacer turn: " + worldModel.getOpponentAngle(12));
 
				try { 
					Thread.sleep(1); // El thread se duerme.
				} catch (InterruptedException ex) {
					System.out.println("Error en " + ex.toString());
                 }
				//Calculate shooting angle and Calculate angle for possible trajectories
				InitShootingAnglesAndTrajectories(p, shootData);
                
                double shootAngle;
                shootAngle=Math.acos((Auxiliar.SQR(shootData.distTop)+Auxiliar.SQR(shootData.distBottom)-Auxiliar.SQR(14))/(2*shootData.distTop*shootData.distBottom));
                
                //Check that the shooting angle is big enough
                if(shootAngle<10){return -2;}
                
                
                //Check that there's no allies nor oponents in possible trajectories
                InitTrajectoryFreedom(worldModel, shootData);

                                                
                //Check what's the best possible trajectory to shoot
                if(p.getY()>0)
                { //Distance is shorter for top trajectory
                	return GetTopTrajectoryValue(worldModel, shootData, kickInfo);
                }
                else
                {
                	return GetBottomTrajectoryValue(worldModel, shootData, kickInfo);
                }
        }                                               
}       
